package com.learn.chapter02;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.LinkedList;

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 * @author: liyf
 * @create: 2025-02-06 14:25
 **/
public class First {

    @Test
    void testStubbing() {

        // 使用具体的类，不使用接口
        LinkedList mockedList = Mockito.mock(LinkedList.class);

        // stubbing
        when(mockedList.get(0)).thenReturn("one");
        when(mockedList.get(1)).thenThrow(new RuntimeException());
        when(mockedList.get(2)).thenThrow(new IndexOutOfBoundsException());

        // get(0) 返回 one
        Assertions.assertEquals("one", mockedList.get(0));

        // 返回 runtimeException 异常
        Assertions.assertThrows(RuntimeException.class, () -> mockedList.get(1));

        // 返回 IndexOutOfBoundsException 异常
        Assertions.assertThrows(IndexOutOfBoundsException.class, () -> mockedList.get(2));

        // 输出 null 因为 get(900) 没有设置 stubbed
        Assertions.assertEquals(null, mockedList.get(900));

        // 虽然可以验证桩(stubbed)调用，但通常这是多余的。
        // 如果你的代码关心`get(0)`返回什么，那么其他地方会出问题（通常甚至在`verify()`执行之前）。
        // 如果你的代码不关心`get(0)`返回什么，那么就不应该对其进行桩处理。
        verify(mockedList).get(0);
    }


    /**
     * 桩行为的覆盖
     * 在Mockito中，桩行为可以通过when()方法定义。如果在测试中对同一个方法多次定义桩行为，后面的桩行为会覆盖前面的桩行为。
     *
     * 1. 初始桩行为将getIntValue()的返回值设置为10。
     * 2. 后续的桩行为将返回值改为20。
     * 3. 最终调用getIntValue()时，返回值为20，即最后定义的桩行为生效。
     */
    @Test
    void testStubbingOverride() {
        DefaultStubbingService mockService = Mockito.mock(DefaultStubbingService.class);

        // 初始桩行为
        when(mockService.getIntValue()).thenReturn(10);
        Assertions.assertEquals(10, mockService.getIntValue());


        // 覆盖桩行为
        when(mockService.getIntValue()).thenReturn(20);
        Assertions.assertEquals(20, mockService.getIntValue());
    }

    /**
     * 桩行为的持久性
     * 一旦对某个方法进行了桩处理，该方法将始终返回定义的桩值，无论它被调用多少次。
     *
     * 无论调用getIntValue()多少次，返回值始终为42，因为桩行为一旦定义，就会一直生效。
     */
    @Test
    void testStubbingPersistence() {
        DefaultStubbingService mockService = Mockito.mock(DefaultStubbingService.class);

        // 初始桩行为
        when(mockService.getIntValue()).thenReturn(42);

        // 第一次调用
        Assertions.assertEquals(42, mockService.getIntValue());

        // 第二次调用
        Assertions.assertEquals(42, mockService.getIntValue());

        // 第三次调用
        Assertions.assertEquals(42, mockService.getIntValue());

    }


    /**
     * 桩行为的顺序
     *
     * 如果对同一个方法且带有相同参数多次进行桩处理，最后定义的桩行为会生效。桩处理的顺序很重要，但这种情况很少见，通常只在以下场景中有意义：
     *
     * 在这些例子中，最后一次定义的桩行为会覆盖之前的桩行为。
     */
    @Test
    void testStubbingOrder() {
        DefaultStubbingService mockService = Mockito.mock(DefaultStubbingService.class);

        // 第一次桩处理
        when(mockService.getStringValue()).thenReturn("First");

        // 第二次桩处理
        when(mockService.getStringValue()).thenReturn("Second");

        Assertions.assertEquals("Second", mockService.getStringValue());
    }
}
